﻿
Imports System.Runtime.CompilerServices
Imports System.Net
Imports std
Imports System.IO

Public Module wwwget
    Private args As map(Of String, vector(Of String)) = Nothing

    Private Class arguments
        Public Shared ReadOnly help() As String = {"h", "help"}
        Public Shared ReadOnly version() As String = {"v", "ver", "version"}
        Public Shared ReadOnly inputfile() As String = {"if", "inputfile"}
        Public Shared ReadOnly outputfile() As String = {"of", "outputfile"}
        Public Shared ReadOnly outputResponseHeaders() As String = {"resph", "responseheaders"}
        Public Shared ReadOnly outputRequestHeaders() As String = {"reqh", "requestheaders"}
        Public Shared ReadOnly method() As String = {"m", "method"}
        Public Shared ReadOnly requestHeaders() As String = {"hd", "header"}
    End Class

    Private Sub version()
        Console.WriteLine(applicationDescription())
    End Sub

    Private Sub help()
        Console.WriteLine(applicationName() +
                          " -inputfile / -outputfile / -responseheaders / -requestheaders / -method / -header")
    End Sub

    Private Function argfind(Of T)(ByVal d As _do(Of map(Of String, vector(Of String)).iterator, T),
                                   ByVal nil As _do(Of T),
                                   ByVal ParamArray ps() As String) As T
        Dim it As map(Of String, vector(Of String)).iterator = Nothing
        it = args.find(ps)
        If it = args.end() Then
            Return do_(nil, Nothing)
        Else
            Return do_(d, it, Nothing)
        End If
    End Function

    Private Function value(ByVal ParamArray ps() As String) As String
        Return argfind(Function(ByRef it As map(Of String, vector(Of String)).iterator) As String
                           Return If((+it).second.size() = 0, Nothing, (+it).second(0))
                       End Function,
                       Function() As String
                           Return Nothing
                       End Function,
                       ps)
    End Function

    Private Function has(ByVal ParamArray ps() As String) As Boolean
        Return argfind(Function(ByRef it As map(Of String, vector(Of String)).iterator) As String
                           Return True
                       End Function,
                       Function() As String
                           Return False
                       End Function,
                       ps)
    End Function

    Private Sub download(ByVal out As TextWriter, ByVal err As TextWriter)
        assert(Not out Is Nothing)
        assert(Not err Is Nothing)

        Dim outputHeadersAction As nvchelper.traversalAction =
                    Sub(name As String, value As String, para As Object)
                        err.WriteLine(name + network.headerNameValueSeparator + character.blank + value)
                    End Sub

        Dim s As String = Nothing
        Dim bs As bytes = Nothing
        Dim rs As Stream = Nothing
        Dim request As WebRequest = Nothing
        s = value(emptyString)
        request = WebRequest.Create(s)
        err.WriteLine(" -- Request " + s)

        s = value(arguments.method)
        If Not isemptystring(s) Then
            request.Method = s
        End If
        err.WriteLine(" -- Method " + request.Method)

        For j As Int64 = 0 To arguments.requestHeaders.Length() - 1
            argfind(Function(ByRef it As map(Of String, vector(Of String)).iterator) As Int32
                        For i As Int64 = 0 To (+it).second.size() - 1
                            request.Headers.Add((+it).second(i))
                        Next
                        Return 0
                    End Function,
                    do_(0),
                    arguments.requestHeaders(j))
        Next

        s = value(arguments.inputfile)
        If Not isemptystring(s) AndAlso readfile(s, bs) Then
            rs = request.GetRequestStream()
            If Not writestream(rs, +bs) Then
                err.WriteLine("cannot write input file " + s + " into request stream.")
            End If
            closestream(rs)
        End If

        If has(arguments.outputRequestHeaders) Then
            err.WriteLine(" -- Request Headers")
            nvchelper.traversal(request.Headers, outputHeadersAction)
        End If

        Dim response As HttpWebResponse = Nothing
        Try
            response = request.GetResponse()
        Catch ex As WebException
            response = ex.Response()
        End Try
        err.WriteLine(" -- Response " + Convert.ToString(response.ResponseUri()))
        err.WriteLine(" -- Status " + Convert.ToString(response.StatusCode()) + " : " +
                                      Convert.ToString(response.StatusDescription()))

        If has(arguments.outputResponseHeaders) Then
            err.WriteLine(" -- Response Headers")
            nvchelper.traversal(response.Headers, outputHeadersAction)
        End If

        rs = response.GetResponseStream()
        If readstream(rs, bs) <> response.ContentLength() Then
            err.WriteLine("cannot read all bytes from response")
        End If

        s = value(arguments.outputfile)
        If isemptystring(s) Then
            Dim enc As Text.Encoding = Nothing
            Try
                enc = Text.Encoding.GetEncoding(response.ContentEncoding)
            Catch
                enc = encoding.utf8()
            End Try
            err.WriteLine(" -- Response Body")
            out.WriteLine(enc.GetString(+bs))
        Else
            Dim os As Stream = Nothing
            os = New FileStream(s, FileMode.Create)
            os.Write(+bs, 0, bs.size())
            closestream(os)
        End If
    End Sub

    Private Sub download()
        download(Console.Out, Console.Error)
    End Sub

    Public Sub main(ByVal iargs() As String)
        debugpause()
        argmgr.parse(iargs, args, lcase:=True)
        If args.size() = 0 OrElse args.find(arguments.help) <> args.end() Then
            help()
        ElseIf args.find(arguments.version) <> args.end() Then
            version()
        Else
            Try
                download()
            Catch ex As Exception
                Console.Error.WriteLine("Unhandled exception <" + ex.GetType().FullName() + ">: " + ex.Message)
                Console.Error.WriteLine(ex.StackTrace())
            End Try
        End If
    End Sub
End Module
